
#import <dpsclient/event.h>
#include <objc/NXBundle.h>
#include <dpsclient/psops.h>

#define NEXT

#import <appkit/OpenPanel.h>
#import <appkit/ClipView.h>
#import <appkit/ScrollView.h>
#import <appkit/Scroller.h>
#import <appkit/Matrix.h>
#import "RasView.h"

#ifdef TWOBIT
char *tbuf;
#endif

@implementation RasView

//
- initFrame:(const NXRect *) frameRect
 {
    [(self = [super initFrame:frameRect]) allocateGState];
    bm = nil;
#if IMAGE
    image = nil;
#endif
    meters = nil;
    text = nil;
    rasView = self;
    XRange = frameRect->size.width;
    YRange = frameRect->size.height;
    [NXApp setDelegate:self];
    return self;
 }

- initBitmap
{
    if (bm) [bm free];
#ifdef TWOBIT
    if (bm) free(tbuf);
    tbuf = malloc(((XRange>>2)+1)*YRange);
    bm = [[NXBitmapImageRep alloc] initData:(unsigned char*)tbuf
	    pixelsWide:XRange
	    pixelsHigh:YRange
	    bitsPerSample:2
	    samplesPerPixel:1
	    hasAlpha:NO
	    isPlanar:NO
	    colorSpace:NX_OneIsWhiteColorSpace
	    bytesPerRow:((XRange>>2)+1)
	    bitsPerPixel:2
    ];
#else
#ifdef EIGHTBIT
//    printf("Initialize 8bit\n");
    bm = [[NXBitmapImageRep alloc] initData:(unsigned char*)FBuffer
	    pixelsWide:XRange
	    pixelsHigh:YRange
	    bitsPerSample:8
	    samplesPerPixel:1
	    hasAlpha:NO
	    isPlanar:NO
	    colorSpace:NX_OneIsWhiteColorSpace
	    bytesPerRow:0
	    bitsPerPixel:0
    ];
#else
#ifdef SIXTEENBIT
//    printf("Initialize 12bit\n");
    bm = [[NXBitmapImageRep alloc] initData:(unsigned char*)FBuffer
	    pixelsWide:XRange
	    pixelsHigh:YRange
	    bitsPerSample:4
	    samplesPerPixel:3
	    hasAlpha:NO
	    isPlanar:NO
	    colorSpace:NX_RGBColorSpace
	    bytesPerRow:0
	    bitsPerPixel:16
    ];
#else
//    printf("Initialize 24bit\n");
    bm = [[NXBitmapImageRep alloc] initData:(unsigned char*)FBuffer
	    pixelsWide:XRange
	    pixelsHigh:YRange
	    bitsPerSample:8
	    samplesPerPixel:3
	    hasAlpha:NO
	    isPlanar:NO
	    colorSpace:NX_OneIsWhiteColorSpace
	    bytesPerRow:0
	    bitsPerPixel:32
    ];
#endif
#endif
#endif
#if IMAGE
    { NXSize sz;
	[bm getSize:&sz];
	image=[[NXImage alloc] initSize:&sz];
	if (![image useRepresentation:bm]) printf("Representation error\n");
//	[image setUnique:YES]; /* make caches disjoint */
//	[image setBackgroundColor:NX_COLORBLACK];
    }
#endif
    return self;
}

#define Button1Mask 1
#define Button2Mask 2
#define Button3Mask 4
#define ACTIVEBUTTONMASK \
    (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK| \
    NX_RMOUSEUPMASK|NX_RMOUSEDRAGGEDMASK)

extern void MouseMove( int status, int dx, int dy );

- (BOOL)acceptsFirstMouse
{
    return YES;
}

- resetMeter:(int)i
{
    [[meters cellAt:i :0] setStringValue:""];
    return self;
}

- setMeter:(int)i value:(Real)t
{
    char buf[32];

    sprintf(buf, "%7.4f", t);
    [[meters cellAt:i :0] setStringValue:buf];
    return self;
}

- keyDown:(NXEvent *)theEvent
{
    [super keyDown:theEvent];
//    printf("keyDown:\n");
    return self;
}

- mouseDown:(NXEvent *)theEvent
{
    int oldMask;
    NXPoint oldMouse, newMouse, dMouse;
    unsigned int status, buttons=0;
    Real t, t0;

    // track the mouse until a mouseUp event occurs, updating the display
    // as tracking happens.

    if (theEvent->type==NX_LMOUSEDOWN) buttons = Button1Mask;
    else if (theEvent->type==NX_RMOUSEDOWN) buttons = Button2Mask;
    [self lockFocus];
    oldMask = [window addToEventMask:ACTIVEBUTTONMASK];
    
    oldMouse = theEvent->location;
    [self convertPoint:&oldMouse fromView:nil];
    [self setMeter:1 value:0.0];
    t = t0 = realtime();
    while (1) {
	newMouse = theEvent->location;
	[self convertPoint:&newMouse fromView:nil];
	dMouse.x = newMouse.x - oldMouse.x;
	dMouse.y = newMouse.y - oldMouse.y;
	if (dMouse.x != 0.0 || dMouse.y != 0.0) {
	    status = (theEvent->flags & (65535<<16)) | buttons;
	    MouseMove(status, (int)dMouse.x, -(int)dMouse.y);
	    theEvent = [NXApp peekAndGetNextEvent:ACTIVEBUTTONMASK];
	    if (!theEvent) {
		RefreshScreen();
		[self setMeter:1 value:realtime()-t0-t];
		t = realtime()-t0;
	    }
	} else theEvent = NULL;
	if (!theEvent) theEvent = [NXApp getNextEvent:ACTIVEBUTTONMASK];
	if (theEvent->type==NX_MOUSEUP || theEvent->type==NX_RMOUSEUP) break;
	oldMouse = newMouse;
    }
	
    [self unlockFocus];
    [self resetMeter:1];
    [window setEventMask:oldMask];
    return self;
}

- rightMouseDown:(NXEvent *)theEvent
{
    return [self mouseDown:theEvent];
}

- superviewSizeChanged:(const NXSize *)oldSize
{
    [super superviewSizeChanged:oldSize];
    XRange = bounds.size.width;
    YRange = bounds.size.height;
    WRange = XRange>>1;
    HRange = YRange>>1;
    Range = MinFun(XRange,YRange);
    printf("Change size to: %d %d\n", XRange, YRange);
    ReDrawFlag |= RFReSize;
    RefreshScreen();
    return self;
}

int drawCount=0;

- drawSelf:(const NXRect *) rects :(int)count
 {
#if IMAGE
    NXPoint origin = {0.0,0.0};

    [image recache];
    [image composite:NX_SOVER toPoint:&origin];
#else
    [bm draw];
#endif
    drawCount++;
    return self;
 }

- (BOOL)openFile:(const char *)fn
{
    if( fn && FetchFile(FileFormat,True, (char *)fn) )
    {   
	ReDrawFlag |= RFRefresh | RFColour;
	if( InfoBondCount < 1 ) EnableBackbone(CylinderFlag,80);
	else EnableWireframe(CylinderFlag,80);
	CPKColourAttrib();
	RefreshScreen();
    } else {
	NXRunAlertPanel(NULL,"Couldn't get molecule from %s", NULL,NULL,NULL, fn);
	fn = NULL;
    }
    if( !CommandActive ) ResetCommandLine(0);
    [self update];
    return fn!=0;
}

- open:sender
 {
//    const char *const types[] = {{"pdb"}, (const char *) NULL};
    id pan = [OpenPanel new];
    const char *const *filenames;
    char filename[FILENAME_MAX];

    if (![pan runModal]) return NO;
    if ((filenames = [pan filenames]) == NULL) return NO;
    sprintf(filename,"%s/%s", [pan directory], filenames[0]);
    [self openFile:filename];
    return self;
}

- update
{
    RefreshScreen();
    return self;
}

- setText:aText
{
    text = aText;
    [text setDelegate:self];
    [text setMonoFont:FALSE];
    [text setCharFilter:NXFieldFilter];
    ras_init();
    FBClear = False;
    ReDrawFlag |= RFRefresh;
    RefreshScreen();
    if (text) [[meters window] setNextResponder:text];
    [self setNextResponder:text];
    [window makeFirstResponder:self];
    [window makeKeyAndOrderFront:self];
    return self;
}

- text
{
    return text;
}

- setMeters:aMatrix;
{
    meters = aMatrix;
    if (text) [[meters window] setNextResponder:text];
    return self;
}

- meters
{
    return meters;
}

- break
{
    int len;
    
    line[lineIndex] = '\0';
    len = [text charLength];
    [text setSel:len :len];
    [text setSelFontStyle:NX_UNBOLD];
    [text replaceSel:line];
    [text scrollSelToVisible];
    lineIndex = 0;
    return self;
}

- putChar:(char)ch
{
    line[lineIndex++] = ch;
    if (ch=='\n' || lineIndex>=linebuflength-1) [self break];
    return self;
}

- show:(char *)str
{
    while(*str) [self putChar:*(str++)];
    [self break];
    return self;
}

// Application's delegate methods

- (BOOL)appAcceptsAnotherFile:sender
{
  return YES;
}

- (int)app:sender openFile:(const char *)filename type:(const char *)type
{
    [self close:sender];
    return [self openFile:filename];
}

// Text's delegate method

- textDidEnd:sender endChar:(unsigned short)whyEnd
{
    NXSelPt start, end;
    int len, ln, cp;
    char *buf;
    
    [window makeFirstResponder:text];
    if (whyEnd==NX_RETURN) {
	[sender getSel:&start :&end];
	ln = [sender lineFromPosition:end.cp-1];
	cp = [sender positionFromLine:ln];
	len = end.cp-cp;
	buf = malloc(len+2);
	[sender getSubstring:buf start:cp length:len];
	ln = 0;
	if (strncmp(buf, CurPrompt, strlen(CurPrompt))==0) ln+=strlen(CurPrompt);
	[text setSel:cp+ln :end.cp];
	[text setSelFontStyle:NX_BOLD];
	buf[len] = '\n';
	buf[len+1] = '\0';
	[self putChar:'\n'];
	for (cp=ln; buf[cp]; cp++)
	    if(ProcessCharacter(buf[cp]) && ProcessCommand()) exit(0);
	if( !CommandActive ) ResetCommandLine(0);
	RefreshScreen();
	free(buf);
    }
    return self;
}

- setValue:sender
{
    switch([sender selectedRow]){
	case 0: ColourDepth = (int)[[sender cellAt:0 :0] floatValue];
	    printf("ColourDepth: %d\n", ColourDepth);
	    break;
	case 1: Ambient = [[sender cellAt:1 :0] floatValue];
	    printf("Ambient: %g\n", Ambient);
	    break;
	default: ;
    }
    return self;
}

- testPattern:sender
{
    int i, j, k, dn;
    Real t;
    Pixel c;
    NXEvent theEvent;

    printf("Test pattern\n");
    ClearBuffers();
    dn = YRange/(LastShade+3);
    for (i=0; i<XRange; i++) {
	c = 1<<(i*32/(XRange+1));
	for (j=0; j<dn; j++) FBuffer[j*XRange+i] = c;
	c = Lut[i*LutSize/(XRange+1)];
	for (j=dn; j<2*dn; j++) FBuffer[j*XRange+i] = c;
	for (k=0; k<LastShade; k++) {
	    c = Lut[Shade2Colour(k)+i*ColourDepth/(XRange+1)];
	    for (j=0; j<dn && j<YRange-dn; j++) FBuffer[((2+k)*dn+j)*XRange+i] = c;
	}
    }
    [self setMeter:0 value:0.0];
    [self setMeter:3 value:0.0];
    t = realtime();
    for (k=0; ![NXApp peekNextEvent:NX_KEYDOWNMASK into:&theEvent]; k++) {
	for (i=0; i<XRange; i++) {
	    c = Lut[LutSize-1-((i+k*XRange/32)%XRange)*LutSize/(XRange+1)];
	    for (j=YRange-dn; j<YRange; j++) FBuffer[j*XRange+i] = c;
	}
//	[self display];
	TransferImage();
	if (k%10==0) {
	    [self setMeter:0 value:10.0/(realtime()-t)];
	    [self setMeter:3 value:(Real)drawCount];
	    t = realtime();
	}
    }
    [self resetMeter:0];
    [self resetMeter:3];
    FBClear = False;
    ReDrawFlag |= RFRefresh;
    RefreshScreen();
    return self;
}

- testRotate:sender;
{
    int k;
    Real t, tf;
    NXEvent theEvent;

    [self setMeter:0 value:0.0];
    [self setMeter:1 value:0.0];
    [self setMeter:3 value:0.0];
    t = tf = realtime();
    for (k=0; ![NXApp peekNextEvent:NX_KEYDOWNMASK|NX_MOUSEDOWNMASK
	into:&theEvent]; k++) {
	    MouseMove( Button1Mask, 5, 0 );
	    RefreshScreen();
	    [self setMeter:1 value:realtime()-t];
	    t = realtime();
	    if (k%10==0) {
		[self setMeter:0 value:10.0/(realtime()-tf)];
		[self setMeter:3 value:drawCount];
		tf = realtime();
	    }
    }
    [self resetMeter:0];
    [self resetMeter:1];
    [self resetMeter:3];
    return self;
}

- close:sender
{
    ZapDatabase();
    return [self update];
}

- wireFrame: sender
{
    DisableSpacefill();
    EnableWireframe(WireFlag,0);
    SetRibbonStatus(False,0,0);
    DisableBackbone();
    ReDrawFlag |= RFRefresh;
    return [self update];
}

- backBone: sender
{
    DisableSpacefill();
    DisableWireframe();
    SetRibbonStatus(False,0,0);
    EnableBackbone(CylinderFlag,80);
    ReDrawFlag |= RFRefresh;
    return [self update];
}

- sticks: sender
{
    DisableSpacefill();
    if( MainAtomCount<256 )
    {   EnableWireframe(CylinderFlag,40);
    } else EnableWireframe(CylinderFlag,80);
    SetRibbonStatus(False,0,0);
    ReDrawFlag |= RFRefresh;
    DisableBackbone();
    return [self update];
}

- spheres: sender
{
    SetVanWaalRadius();
                                SetVanWaalRadius();
                                DisableWireframe();
                                SetRibbonStatus(False,0,0);
                                DisableBackbone();
                                ReDrawFlag |= RFRefresh;
    return [self update];
}

- ballAndStick: sender
{
                                SetRadiusValue(120);
                                EnableWireframe(CylinderFlag,40);
                                SetRibbonStatus(False,0,0);
                                DisableBackbone();
                                ReDrawFlag |= RFRefresh;
    return [self update];
}


- ribbons: sender
{
                                DisableSpacefill();
                                DisableWireframe();
                                SetRibbonStatus(True,RibbonFlag,0);
                                DisableBackbone();
                                ReDrawFlag |= RFRefresh;
    return [self update];
}

- strands: sender
{
                                DisableSpacefill();
                                DisableWireframe();
                                SetRibbonStatus(True,StrandFlag,0);
                                DisableBackbone();
                                ReDrawFlag |= RFRefresh;
    return [self update];
}

- cartoons: sender
{
                                DisableSpacefill();
                                DisableWireframe();
                                SetRibbonCartoons();
                                DisableBackbone();
                                ReDrawFlag |= RFRefresh;
    return [self update];
}

- mono: sender
{
    MonoColourAttrib(255,255,255);
    ReDrawFlag |= RFColour;
    return [self update];
}

- cpk: sender
{
    CPKColourAttrib();
    ReDrawFlag |= RFColour;
    return [self update];
}

- shapely: sender
{
    ShapelyColourAttrib();
    ReDrawFlag |= RFColour;
    return [self update];
}

- group: sender
{
    ScaleColourAttrib( GroupAttr );
    ReDrawFlag |= RFColour;
    return [self update];
}

- chain: sender
{
    ScaleColourAttrib( ChainAttr );
    ReDrawFlag |= RFColour;
    return [self update];
}

- temp: sender
{
    ScaleColourAttrib( TempAttr );
    ReDrawFlag |= RFColour;
    return [self update];
}

- struct: sender
{
    StructColourAttrib();
    ReDrawFlag |= RFColour;
    return [self update];
}

- user: sender
{
    UserMaskAttrib(MaskColourFlag);
    ReDrawFlag |= RFColour;
    return [self update];
}

- slabbing: sender
{
    ReDrawFlag |= RFRefresh;
    UseSlabPlane = !UseSlabPlane;
    if( UseSlabPlane ) UseShadow = False;
    return [self update];
}

- hydrogens: sender
{
    int mask = NormAtomFlag;
    if( HetaGroups ) mask |= HeteroFlag;
    Hydrogens = !Hydrogens;
    ReDrawFlag |= RFRefresh;
    if( Hydrogens ) SelectZone(mask|HydrogenFlag);
    else RestrictZone(mask);
    return [self update];
}

- hetero: sender
{
    int mask = NormAtomFlag;
    if( Hydrogens ) mask |= HydrogenFlag;
    HetaGroups = !HetaGroups;
    ReDrawFlag |= RFRefresh;
    if( HetaGroups ) SelectZone(mask|HeteroFlag);
    else RestrictZone(mask);
    return [self update];
}

- specular: sender
{
    FakeSpecular = !FakeSpecular;
    ReDrawFlag |= RFColour;
    return [self update];
}

- shadows: sender
{
    ReDrawFlag |= RFRefresh;
    UseShadow = !UseShadow;
    if( UseShadow ) {
	ReviseInvMatrix();
	VoxelsClean = False;
	UseSlabPlane = False;
	ReAllocBuffers();
    }
    return [self update];
}

- stereo: sender
{
                                if( UseStereo )
                                {   SetStereoMode(False);
                                } else SetStereoMode(True);
                                ReDrawFlag |= RFRefresh;
    return [self update];
}

- labels: sender
{
                                LabelOptFlag = !LabelOptFlag;
                                DefaultLabels(LabelOptFlag);
                                ReDrawFlag |= RFRefresh;
    return [self update];
}

- export: sender
{
    ResetCommandLine(3);
    return self;
}

- help: sender
{
    return self;
}

@end
